/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.rpc.client;

import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.common.JSONSerializer;
import io.iec.edp.caf.commons.exception.CAFRuntimeException;
import io.iec.edp.caf.commons.exception.entity.DefaultExceptionProperties;
import io.iec.edp.caf.commons.exception.entity.ExceptionErrorCode;
import io.iec.edp.caf.commons.exception.ExceptionLevel;
import io.iec.edp.caf.commons.runtime.CafEnvironment;

import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.core.session.ICafSessionService;
import io.iec.edp.caf.msu.api.ServiceUnitAwareService;

import io.iec.edp.caf.msu.client.exception.ServiceUnitNotFoundException;
import io.iec.edp.caf.rpc.api.entity.RpcReturnValueDefinition;
import io.iec.edp.caf.rpc.api.entity.RpcServiceMethodDefinition;

import io.iec.edp.caf.rpc.api.support.ConstanceVarible;
import io.iec.edp.caf.rpc.api.support.RpcThreadCacheHolder;
import io.iec.edp.caf.rpc.api.support.RpcTimeoutHolder;
import io.iec.edp.caf.rpc.api.utils.Validator;
import io.iec.edp.caf.rpc.client.local.RpcLocalInvoker;
import io.iec.edp.caf.rpc.api.serialize.RpcSerializeUtil;
import io.iec.edp.caf.rpc.api.service.InternalServiceManageService;
import io.iec.edp.caf.rpc.api.service.RpcClient;
import io.iec.edp.caf.rpc.api.support.Type;

import io.iec.edp.caf.rpc.remote.http.discover.RpcAddressDiscover;
import io.iec.edp.caf.rpc.server.invoker.RpcRemoteInvoker;
import io.iec.edp.caf.tenancy.api.ITenantService;
import io.iec.edp.caf.tenancy.api.ITenantRouteService;
import io.iec.edp.caf.tenancy.api.context.RequestTenantContextHolder;
import io.iec.edp.caf.tenancy.api.context.RequestTenantContextInfo;
import io.iec.edp.caf.tenancy.api.entity.TenancyMode;
import io.iec.edp.caf.tenancy.api.exception.TenantNotFoundException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.cxf.jaxrs.ext.multipart.Multipart;
import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;

/**
 * @author Leon Huo
 */
@Slf4j
public class RpcClientImpl implements RpcClient {

    private ServiceUnitAwareService serviceUnitAware;

    private InternalServiceManageService management;

    private RpcLocalInvoker rpcLocalInvoker;

    private RpcRemoteInvoker rpcRemoteInvoker;
    private ITenantService tenantService;
    private ITenantRouteService tenantRouteService;

    private RpcAddressDiscover rpcAddressDiscover;

    private String cookieName;

    public RpcClientImpl(InternalServiceManageService management, RpcLocalInvoker rpcLocalInvoker,
                         RpcRemoteInvoker rpcRemoteInvoker, ServiceUnitAwareService serviceUnitAware, ITenantService tenantService,
                         ITenantRouteService tenantRouteService, RpcAddressDiscover rpcAddressDiscover) {


        this.management = management;
        this.rpcRemoteInvoker = rpcRemoteInvoker;
        this.rpcLocalInvoker = rpcLocalInvoker;
        this.serviceUnitAware = serviceUnitAware;
        this.cookieName = CafEnvironment.getEnvironment().getProperty("caf-security.general.cookie-name");
        this.tenantService = tenantService;
        this.tenantRouteService = tenantRouteService;
        this.rpcAddressDiscover = rpcAddressDiscover;
        if (this.cookieName == null) {
            this.cookieName = ConstanceVarible.COOKIE_SESSION_NAME;
        }
    }


    /**
     * invoke rpc service
     * @param clazz           return value type
     * @param serviceId       serviceid
     * @param serviceUnitName msu code
     * @param parameters      service parameters
     * @param context         service context
     * @param <T>
     * @return rpc service return value
     * @throws MalformedURLException
     */
    @Override
    public <T> T invoke(Class<T> clazz, String serviceId, String serviceUnitName, LinkedHashMap<String, Object> parameters,
           HashMap<String, String> context) {
//        return this.invokeService(clazz, serviceId, serviceUnitName, parameters, context);
        return this.invoke(new Type<>(clazz), serviceId, serviceUnitName, parameters, context);

    }

    /**
     * invoke rpc service
     * @param serviceId       serveiceid
     * @param serviceUnitName msu code
     * @param parameters      service parameters
     * @param context         invoke context
     * @return the rpc service return value
     * @throws MalformedURLException
     */
    @Override
    public String invoke(String serviceId, String serviceUnitName, LinkedHashMap<String, Object> parameters,
                         HashMap<String, String> context) {
        return this.invoke(String.class, serviceId, serviceUnitName, parameters, context);
    }


    /**
     * invoke rpc service
     * @param t           return value type
     * @param serviceId       serviceid
     * @param serviceUnitName msu code
     * @param parameters      service parameters
     * @param context         service context
     * @param <T>
     * @return rpc service return value
     */
    @SneakyThrows
    @Override
    public <T> T invoke(Type<T> t, String serviceId, String serviceUnitName, LinkedHashMap<String, Object> parameters, HashMap<String, String> context) {
        try {
            var start = System.currentTimeMillis();
            var end =  System.currentTimeMillis();

            //get cookies
            Cookie[] cks = this.getCookies();
            //check cookie only log warning not break invoke
            Validator.CheckCksSession(cks,serviceId);

            T result;
            //get rpc service definition from local memory variable
            RpcServiceMethodDefinition serviceMethodDefinition = this.management.getRpcMethodDefinition(serviceId);
            //get extend tenant id
            Integer targetTenantId = getTargetTenantId(serviceUnitName, context);

            //rpc service event context
            HashMap<String, String> eventContext = new HashMap<>();
            eventContext.put(ConstanceVarible.CURRENT_SERVICE_ID, serviceId);
            eventContext.put(ConstanceVarible.TARGET_SU,serviceUnitName);

            log.info("start rpc invoke，current serviceId：{}，current target su:{}",serviceId,serviceUnitName);
            Object retvalue = null;

            //is local invoke
            if (isLocalInvoke(serviceUnitName)) {
                //local memory has no rpc service definition
                if (serviceMethodDefinition == null) {
                    log.error("can not find rpc service by serviceid:"+serviceId+"，can not local invoke,current su："+serviceUnitName);
                    throw new CAFRuntimeException(DefaultExceptionProperties.SERVICE_UNIT,
                            DefaultExceptionProperties.RESOURCE_FILE,
                            ExceptionErrorCode.localServiceUnitNotFound,
                            new String[]{serviceId, serviceUnitName},
                            null, ExceptionLevel.Error, false);
                }

                eventContext.put(ConstanceVarible.IS_LOCAL, "true");

                if(log.isInfoEnabled()){
                    log.info("rpc local invoke info：",JSONSerializer.serialize(serviceMethodDefinition));
                }

                //local invoke（bean first,then reflect）
                start = System.currentTimeMillis();
                result = this.rpcLocalInvoker.invokeLocalService(t.getRawType(), serviceId, serviceUnitName, parameters, serviceMethodDefinition, targetTenantId,eventContext);
                end =  System.currentTimeMillis();
                log.info("rpc local invoke client total time：{} ms",end-start);

                RpcReturnValueDefinition returnValueDefinition = serviceMethodDefinition == null ? null : serviceMethodDefinition.getReturnInfo();
                start = System.currentTimeMillis();
                retvalue = RpcSerializeUtil.deSerializeReturnValue(t, RpcSerializeUtil.serializeReturnValue(result, returnValueDefinition), serviceMethodDefinition);
                end =  System.currentTimeMillis();
                log.info("rpc local invoke client Deserialize time：{} ms",end-start);


            } else {
                //remote invoke
                String remoteUrl = this.rpcAddressDiscover.getAddress(serviceUnitName,eventContext);

                eventContext.put(ConstanceVarible.IS_LOCAL, "false");
                eventContext.put(ConstanceVarible.CURRENT_REMOTEBASE_URL,remoteUrl);

                log.info("rpc remote invoke baseurl：{}",remoteUrl);
                start = System.currentTimeMillis();
                retvalue = this.rpcRemoteInvoker.invokeRemoteService(t, serviceId, serviceUnitName, remoteUrl, parameters, cks, serviceMethodDefinition, targetTenantId,eventContext);
                end =  System.currentTimeMillis();
                log.info("rpc remote invoke client total time:{} ms",end-start);

            }

            return (T)retvalue;
        } catch (Exception e) {

            log.debug("rpc client exception type:{}",e.getClass().getName());
            if(e instanceof ServiceUnitNotFoundException){
                throw e;
            }else if(e instanceof CAFRuntimeException && ((CAFRuntimeException)e).isBizException()){
                throw e;
            }else{
                throw new CAFRuntimeException(DefaultExceptionProperties.SERVICE_UNIT,
                        DefaultExceptionProperties.RESOURCE_FILE,
                        ExceptionErrorCode.rpcError,
                        new String[]{}, e, ExceptionLevel.Error, false);
            }
        }finally {
            RpcThreadCacheHolder.clear();
        }
    }

    /**
     * invoke rpc service by use remote url
     * @param t                     return value type
     * @param serviceId             serviceid
     * @param serviceUnitName       msu code
     * @param parameters            service parameters
     * @param context               service context
     * @param remoteUrl             remote invoke url
     * @return result   rpc service return value
     */
    @Override
    public <T> T invoke(Type<T> t, String serviceId, String serviceUnitName, LinkedHashMap<String, Object> parameters, HashMap<String, String> context, String remoteUrl) {
        try {
            var start = System.currentTimeMillis();
            var end =  System.currentTimeMillis();

            //get cookie
            Cookie[] cks = this.getCookies();
            //check cookie only log warning not break invoke
            Validator.CheckCksSession(cks,serviceId);

            T result;
            //get rpc service definition from local memory variable
            RpcServiceMethodDefinition serviceMethodDefinition = this.management.getRpcMethodDefinition(serviceId);
            //get extend tenant id
            Integer targetTenantId = getTargetTenantId(serviceUnitName, context);

            //rpc service event context
            HashMap<String, String> eventContext = new HashMap<>();
            eventContext.put(ConstanceVarible.CURRENT_SERVICE_ID, serviceId);
            eventContext.put(ConstanceVarible.TARGET_SU,serviceUnitName);
            eventContext.put(ConstanceVarible.IS_LOCAL, "false");
            eventContext.put(ConstanceVarible.CURRENT_REMOTEBASE_URL,remoteUrl);

            log.info("start rpc invoke，current serviceId：{}，current target su:{},remote base url:{}",serviceId,serviceUnitName,remoteUrl);
            start = System.currentTimeMillis();
            result = this.rpcRemoteInvoker.invokeRemoteService(t, serviceId, serviceUnitName, remoteUrl, parameters, cks, serviceMethodDefinition, targetTenantId,eventContext);
            end =  System.currentTimeMillis();
            log.info("rpc remote invoke client total time：{} ms",end-start);

            RpcReturnValueDefinition returnValueDefinition = serviceMethodDefinition == null ? null : serviceMethodDefinition.getReturnInfo();
            start = System.currentTimeMillis();
            var retvalue = RpcSerializeUtil.deSerializeReturnValue(t, RpcSerializeUtil.serializeReturnValue(result, returnValueDefinition), serviceMethodDefinition);
            end =  System.currentTimeMillis();
            log.info("rpc client result serialize time：{} ms",end-start);

            return retvalue;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
//            log.info("rpc client exception type:"+e.getClass().getName());
//            if(e instanceof ServiceUnitNotFoundException){
//                throw e;
//            }else if(e instanceof CAFRuntimeException && ((CAFRuntimeException)e).isBizException()){
//                throw e;
//            }else{
//                throw new CAFRuntimeException(DefaultExceptionProperties.SERVICE_UNIT,
//                        DefaultExceptionProperties.RESOURCE_FILE,
//                        ExceptionErrorCode.rpcError,
//                        new String[]{}, e, ExceptionLevel.Error, false);
//            }
        }finally {
            RpcThreadCacheHolder.clear();
        }
    }

    @SneakyThrows
    @Override
    public InputStream invokeStream(String serviceId,String version, String serviceUnitName, LinkedHashMap<String, Object> parameters, HashMap<String, String> context) {
        try {
            var start = System.currentTimeMillis();
            var end =  System.currentTimeMillis();

            //get cookies
            Cookie[] cks = this.getCookies();
            //check cookie only log warning not break invoke
            Validator.CheckCksSession(cks,serviceId);

            InputStream result;
            //get rpc service definition from local memory variable
            RpcServiceMethodDefinition serviceMethodDefinition = this.management.getRpcMethodDefinition(serviceId);
            //get extend tenant id
            Integer targetTenantId = getTargetTenantId(serviceUnitName, context);

            //rpc service event context
            HashMap<String, String> eventContext = new HashMap<>();
            eventContext.put(ConstanceVarible.CURRENT_SERVICE_ID, serviceId);
            eventContext.put(ConstanceVarible.TARGET_SU,serviceUnitName);

            log.info("start rpc invoke，current serviceId：{}，current target su:{}",serviceId,serviceUnitName);
            //is local invoke
            if (isLocalInvoke(serviceUnitName)) {
                //local memory has no rpc service definition
                if (serviceMethodDefinition == null) {
                    log.error("can not find rpc service by serviceid:"+serviceId+"，can not local invoke,current su："+serviceUnitName);
                    throw new CAFRuntimeException(DefaultExceptionProperties.SERVICE_UNIT,
                            DefaultExceptionProperties.RESOURCE_FILE,
                            ExceptionErrorCode.localServiceUnitNotFound,
                            new String[]{serviceId, serviceUnitName},
                            null, ExceptionLevel.Error, false);
                }

                eventContext.put(ConstanceVarible.IS_LOCAL, "true");

                if(log.isInfoEnabled()){
                    log.info("rpc local invoke info：",JSONSerializer.serialize(serviceMethodDefinition));
                }

                //this method local invoke need use remote localhost invoke to create response
                String remoteUrl = this.getLocalRemoteUrl();

                //local invoke（bean first,then reflect）
                start = System.currentTimeMillis();
//                result = this.rpcLocalInvoker.invokeLocalServiceStream(serviceId,version, serviceUnitName,
//                        parameters, serviceMethodDefinition, targetTenantId,eventContext);
                result = this.rpcRemoteInvoker.invokeRemoteServiceStream(serviceId,version, serviceUnitName,
                        remoteUrl, parameters, cks, serviceMethodDefinition, targetTenantId,eventContext);
                end =  System.currentTimeMillis();
                log.info("rpc local invoke client total time：{} ms",end-start);

            } else {
                //remote invoke
                String remoteUrl = this.rpcAddressDiscover.getAddress(serviceUnitName,eventContext);

                eventContext.put(ConstanceVarible.IS_LOCAL, "false");
                eventContext.put(ConstanceVarible.CURRENT_REMOTEBASE_URL,remoteUrl);

                log.info("rpc remote invoke baseurl：{}",remoteUrl);
                start = System.currentTimeMillis();
                result = this.rpcRemoteInvoker.invokeRemoteServiceStream(serviceId,version, serviceUnitName,
                        remoteUrl, parameters, cks, serviceMethodDefinition, targetTenantId,eventContext);
                end =  System.currentTimeMillis();
                log.info("rpc remote invoke client total time:{} ms",end-start);

            }


            return result;
        } catch (Exception e) {

            log.debug("rpc client exception type:{}",e.getClass().getName());
            if(e instanceof ServiceUnitNotFoundException){
                throw e;
            }else if(e instanceof CAFRuntimeException && ((CAFRuntimeException)e).isBizException()){
                throw e;
            }else{
                throw new CAFRuntimeException(DefaultExceptionProperties.SERVICE_UNIT,
                        DefaultExceptionProperties.RESOURCE_FILE,
                        ExceptionErrorCode.rpcError,
                        new String[]{}, e, ExceptionLevel.Error, false);
            }
        }finally {
            RpcThreadCacheHolder.clear();
        }
    }

    @SneakyThrows
    @Override
    public <T> T invokeStream(InputStream stream, Type<T> t, String serviceId, String version, String serviceUnitName, LinkedHashMap<String, Object> parameters, HashMap<String, String> context) {
        try {
            var start = System.currentTimeMillis();
            var end =  System.currentTimeMillis();

            //get cookies
            Cookie[] cks = this.getCookies();
            //check cookie only log warning not break invoke
            Validator.CheckCksSession(cks,serviceId);

            T result;
            //get rpc service definition from local memory variable
            RpcServiceMethodDefinition serviceMethodDefinition = this.management.getRpcMethodDefinition(serviceId);
            //get extend tenant id
            Integer targetTenantId = getTargetTenantId(serviceUnitName, context);

            //rpc service event context
            HashMap<String, String> eventContext = new HashMap<>();
            eventContext.put(ConstanceVarible.CURRENT_SERVICE_ID, serviceId);
            eventContext.put(ConstanceVarible.TARGET_SU,serviceUnitName);

            log.info("start rpc invoke，current serviceId：{}，current target su:{}",serviceId,serviceUnitName);
            //is local invoke
            if (isLocalInvoke(serviceUnitName)) {
                //local memory has no rpc service definition
                if (serviceMethodDefinition == null) {
                    log.error("can not find rpc service by serviceid:"+serviceId+"，can not local invoke,current su："+serviceUnitName);
                    throw new CAFRuntimeException(DefaultExceptionProperties.SERVICE_UNIT,
                            DefaultExceptionProperties.RESOURCE_FILE,
                            ExceptionErrorCode.localServiceUnitNotFound,
                            new String[]{serviceId, serviceUnitName},
                            null, ExceptionLevel.Error, false);
                }

                eventContext.put(ConstanceVarible.IS_LOCAL, "true");

                if(log.isInfoEnabled()){
                    log.info("rpc local invoke info：",JSONSerializer.serialize(serviceMethodDefinition));
                }

                //local invoke（bean first,then reflect）
                start = System.currentTimeMillis();
                result = this.rpcLocalInvoker.invokeLocalServiceStream(stream,t.getRawType(),serviceId,version,
                        serviceUnitName, parameters, serviceMethodDefinition, targetTenantId,eventContext);
                end =  System.currentTimeMillis();
                log.info("rpc local invoke client total time：{} ms",end-start);

            } else {
                //remote invoke
                String remoteUrl = this.rpcAddressDiscover.getAddress(serviceUnitName,eventContext);

                eventContext.put(ConstanceVarible.IS_LOCAL, "false");
                eventContext.put(ConstanceVarible.CURRENT_REMOTEBASE_URL,remoteUrl);

                log.info("rpc remote invoke baseurl：{}",remoteUrl);
                start = System.currentTimeMillis();
                result = this.rpcRemoteInvoker.invokeRemoteServiceStream(stream, t,serviceId,version,
                        serviceUnitName, remoteUrl, parameters, cks, serviceMethodDefinition, targetTenantId,eventContext);

                end =  System.currentTimeMillis();
                log.info("rpc remote invoke client total time:{} ms",end-start);
            }

            RpcReturnValueDefinition returnValueDefinition = serviceMethodDefinition == null ? null : serviceMethodDefinition.getReturnInfo();
            start = System.currentTimeMillis();
            var retvalue = RpcSerializeUtil.deSerializeReturnValue(t, RpcSerializeUtil.serializeReturnValue(result, returnValueDefinition), serviceMethodDefinition);
            end =  System.currentTimeMillis();
            log.info("rpc client result serialize time：{} ms",end-start);

            return retvalue;
        } catch (Exception e) {

            log.debug("rpc client exception type:{}",e.getClass().getName());
            if(e instanceof ServiceUnitNotFoundException){
                throw e;
            }else if(e instanceof CAFRuntimeException && ((CAFRuntimeException)e).isBizException()){
                throw e;
            }else{
                throw new CAFRuntimeException(DefaultExceptionProperties.SERVICE_UNIT,
                        DefaultExceptionProperties.RESOURCE_FILE,
                        ExceptionErrorCode.rpcError,
                        new String[]{}, e, ExceptionLevel.Error, false);
            }
        }finally {
            RpcThreadCacheHolder.clear();
        }
    }

    /**
     * @return 是否开启https
     */
    private String getLocalRemoteUrl() {
        Environment environment = CafEnvironment.getEnvironment();
        var port = environment.getProperty("server.port");
        var isSSL = !io.iec.edp.caf.commons.utils.StringUtils.isEmpty(environment.getProperty("server.ssl.key-store"));
        var address = "localhost:"+port;

        return isSSL ? "https://" + address:"http://" + address;
    }

    /**
     * invoke rpc service
     * @param <T>
     * @param clazz           return value type
     * @param serviceId       serveiceid
     * @param serviceUnitName msu code
     * @param parameters      service parameters
     * @param context         service context
     * @return rpc service return value
     */
    @Deprecated
    @Override
    public <T> T invokeService(Class<T> clazz, String serviceId, String serviceUnitName, HashMap<String, Object> parameters,
                               HashMap<String, String> context) {
        LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<>(parameters);
        return this.invoke(new Type<>(clazz), serviceId, serviceUnitName, linkedHashMap, context);
    }

    /**
     * invoke rpc service
     * @param serviceId       serveiceid
     * @param serviceUnitName msu code
     * @param parameters      service parameters
     * @param context         service context
     * @return rpc service return value
     */
    @Override
    @Deprecated
    public String invokeService(String serviceId, String serviceUnitName, HashMap<String, Object> parameters,
                                HashMap<String, String> context) {
        return this.invokeService(String.class, serviceId, serviceUnitName, parameters, context);
    }


    /**
     * invoke rpc service
     * @param serviceId       serviceid
     * @param serviceUnitName msu code
     * @param parameters      service parameters
     * @param context         service context
     * @return rpc service return value
     */
    @Override
    @Deprecated
    public String InvokeService(String serviceId, String serviceUnitName, HashMap<String, Object> parameters,
                                HashMap<String, String> context) {
        return this.invokeService(serviceId, serviceUnitName, parameters, context);
    }


    /**
     * set rpc timeout
     * @param timeout timeout(ms)
     */
    @Override
    public void setTimeout(Integer timeout) {
        RpcTimeoutHolder.setTimeout(timeout);
    }


    /**
     * get cookie
     * @return if the request has cookie and has caf session,then use the request cookie
     *         if request has no cookie then use {@link CAFContext} session create a new cookie
     * @throws UnsupportedEncodingException
     */
    private Cookie[] getCookies() throws UnsupportedEncodingException {
        Cookie[] cks = null;
        boolean containSessionId = false;
        //get request cookies
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        var sessionid = "";
        if (servletRequestAttributes != null) {
            HttpServletRequest request = servletRequestAttributes.getRequest();
            cks = request.getCookies();
            //if cookie has session,then use cookie session
            if(cks!=null && cks.length>0){
                for (Cookie cookie: cks) {
                    if(cookie.getName().equalsIgnoreCase(this.cookieName)){
                        containSessionId = true;
                        sessionid = cookie.getValue();
                        break;
                    }
                }
            }
        }

        var sessionsrv = SpringBeanUtils.getBean(ICafSessionService.class);

        if(log.isInfoEnabled()){
            log.info("rpc invoke requst has cookie:"+containSessionId);
            log.info("rpc get sessionid:"+ (sessionid != null ? new String(Base64.getDecoder().decode(sessionid), StandardCharsets.UTF_8) : sessionid)+" from cookie");
            log.info("rpc get sessionid from cookie isExpired:"+ (!"".equals(sessionid) ? sessionsrv.isExpired(new String(Base64.getDecoder().decode(sessionid), StandardCharsets.UTF_8)) : true));
            log.info("rpc get sessionid:"+CAFContext.current.getSessionId()+" from cafcontext");
            log.info("rpc get sessionid from cafcontext isExpired："+((CAFContext.current.getSessionId()!=null&&!"".equals(CAFContext.current.getSessionId())) ? sessionsrv.isExpired(CAFContext.current.getSessionId()) : true));
        }

        if(!containSessionId){
            cks = new Cookie[1];
            cks[0] = new Cookie(cookieName, base64Encode(CAFContext.current.getSessionId()));
        }
        return cks;
    }

    private String base64Encode(String value) throws UnsupportedEncodingException {

        if (org.springframework.util.StringUtils.isEmpty(value)) {
            return "";
        }

        byte[] encodedCookieBytes = Base64.getEncoder().encode(value.getBytes("UTF-8"));
        return new String(encodedCookieBytes);
    }

    /**
     * check is local invoke
     * @param serviceUnitName msu code
     * @return true:local invoke,false: remote invoke
     */
    private boolean isLocalInvoke(String serviceUnitName) {
        return this.serviceUnitAware.getEnabledServiceUnits().stream().anyMatch(d -> d.equalsIgnoreCase(serviceUnitName));
    }

    /**
     * get extend tenantid to route
     * by implement {@link ITenantRouteService} interface
     * @param msu msu code
     * @param context extend parameters to choose route tenant
     * @return if {@link ITenantRouteService} implement it will reture the ITenantRouteService`s tenantid,if not implement
     *          {@link ITenantRouteService} it will found tenantid in http header by CAFContextFilter,if both not found it
     *          will return null
     */
    private Integer getTargetTenantId(String msu, HashMap<String, String> context) {

        if (this.tenantService.getTenancyMode() != TenancyMode.group)
            return null;

        context = context == null ? new HashMap<>() : context;
        String tenantIdString = context.get(ConstanceVarible.ROUTE_TENANTID);
        if (tenantIdString != null) {
            return Integer.valueOf(tenantIdString);
        } else {
            String tenantDim1 = context.get(ConstanceVarible.ROUTE_FIRSTDIMENSION_KEY);
            String tenantDim2 = context.get(ConstanceVarible.ROUTE_SECONDDIMENSION_KEY);
            if (!StringUtils.isEmpty(tenantDim1)) {
                try {
                    return tenantRouteService.route(msu, tenantDim1, tenantDim2);
                } catch (TenantNotFoundException e) {
                    log.error(String.format("tenant route failed su：%s first dimension：%s ,second dimension：%s", msu, tenantDim1, tenantDim2));
                }
            }

            //get requset header tenant if set
            RequestTenantContextInfo contextInfo = RequestTenantContextHolder.get();
            return contextInfo == null ? null : contextInfo.getTenantId();
        }
    }
}
